package wili.logic;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import wili.c.Ca;
import wili.c.Str9a;

/**
 * 服务于：单行逻辑式 ~>缩进式
 */
public class ToIndentLogicNode {
	public final int id = ++GenID;

	public final int start, end;

	public String expSt;

	/**
	 * 此时 expSt和 sons未同步，打印时优先取用 expSt.
	 */
	public boolean isExpUpdated = false;//风险：结点的表达式刷新会覆盖后代的表达式刷新。除非刷新同步到结点。

	Cont21 cont;

	public List<ToIndentLogicNode> sons;

	ToIndentLogicNode dad;

	public Boolean isAnd, isYes;

	final String op;

	public String op() {
		if (this.isAnd) {
			if (isYes)
				return Cont21.AND;
			return Cont21.AND_NOT;
		} else {
			if (isYes)
				return Cont21.OR;
			return Cont21.OR_NOT;
		}
	}

	public String toString() {
		if (sons == null) {
			String st = "da".isEmpty() ? "" : "i" + id + ":";
			return st + expSt + ":" + start + "~" + end;
		}
		StringBuilder sb = new StringBuilder();
		sb.append(op() + "(");
		for (int i = 0; i < sons.size(); i++) {
			ToIndentLogicNode son = sons.get(i);
			if (i > 0)
				sb.append(", ");
			sb.append(son.expSt);
		}
		sb.append(")");
		return sb.toString();
	}

	public ToIndentLogicNode(String expSt) {
		this(new Cont21().setExprSt(expSt), 0, expSt.length());
	}

	public ToIndentLogicNode(Cont21 cont, int start, int end) {
		/*
		 	复合命题是由若干算符连接起来的若干命题，命题是括号或叶，括号内是复合命题或叶。
		 	依次 splitBy or, and, mathOp, 跳过括号范围。
		 	递归地为括号生成结点。
		 	结点可能是算术运算符，当递归深入其左右数
		 */
		end = Str9a.lastIndexOfNotSpace(cont.expSt, start, end) + 1;//略过最右端的空格
		Boolean isAnd2 = null, isYes2 = true;
		String op2 = null;
		this.cont = cont;
		final String rootSt = cont.expSt;
		String sp = Str9a.readSpaces(rootSt, start);
		start += sp.length();
		{//起于左括，止于对应的右括
			char ch = rootSt.charAt(start);
			int start2 = start;
			if (ch == '!' && rootSt.charAt(start + 1) == '(') {
				isYes2 = false;
				start2 = start + 1;
			}
			if (rootSt.charAt(start2) == '(') {
				if (cont.lrIndMap.get(start2) == end - 1) {
					start = start2 + 1;
					end -= 1;
				}
			}
			this.start = start;
			this.end = end;
		}
		this.expSt = rootSt.substring(start, end).trim();
		if (!this.expSt.contains(cont.andOp) && !this.expSt.contains(cont.orOp)) {
			this.isAnd = this.isYes = null;
			this.op = null;
			return;
		}
		Ca.log(-1, "expSt:" + expSt);
		int from = start;
		String[] andOrOp = { cont.orOp, cont.andOp };
		for (int k = 0; k < andOrOp.length; k++) {
			String logicOp = andOrOp[k];
			for (int i = start; i < end; i++) {
				char ch = rootSt.charAt(i);
				if (i == 410)
					"".isEmpty();
				Ca.log(-1, i, "#", rootSt.substring(i));
				switch (ch) {
				case '\'':
				case '"': {
					String qt = Str9a.readQuote(rootSt, i);
					i += qt.length() - 1;
					continue;
				}
				case '(': {
					int rInd = cont.lrIndMap.get(i);
					i = rInd;
					continue;
				}
				default: {
					if (rootSt.startsWith("|{ObjType} eq 22", i))
						Coms21.pause();
					if (rootSt.startsWith(logicOp, i)) { //生成逻辑逻算符左侧的结点。
						op2 = logicOp;
						isAnd2 = k == 1;
						String st = rootSt.substring(from, i).trim();
						if (st.startsWith("false "))
							Coms21.pause();
						if (st.isEmpty()) {
							throw new RuntimeException("Invalid expr: " + rootSt.substring(start));
						} else {
							this.addSon(new ToIndentLogicNode(cont, from, i));//生成算符左侧结点
						}
						from = i + logicOp.length();
						i = from - 1;
					}
				}
				}
			}
			if (sons == null) {
				Ca.pause();
			} else {//有子，必是或式|与式
				Ca.asert(op2 != null, "");
				if (from < end) {
					this.addSon(new ToIndentLogicNode(cont, from, end));//生成算符右侧结点。
				} else {
					throw new RuntimeException("Invalid expr: " + rootSt.substring(start, end));
				}
				break;
			}
		}
		this.isAnd = isAnd2;
		this.isYes = isYes2;
		this.op = op2;
	}

	public void traverse(Predicate<ToIndentLogicNode> pred) {
		if (pred.test(this))
			return;
		if (sons == null)
			return;
		for (int i = 0; i < sons.size(); i++) {
			ToIndentLogicNode son = sons.get(i);
			son.traverse(pred);
		}
	}

	void toIndentString00(String dent, String pre, StringBuilder sb) {
		//父决定了子的缩进
		if (sons == null || this.isExpUpdated) {
			String st = "".isEmpty() ? "" : "i" + id + ":";
			sb.append(dent + pre + st + expSt);
			return;
		} else if (this.isAnd != null) {
			if (this.isAnd) {
				sb.append(dent + (this.isYes ? Cont21.AND : Cont21.AND_NOT));
			} else {
				sb.append(dent + (this.isYes ? Cont21.OR : Cont21.OR_NOT));
			}
			dent += Dent1;
			dent += " ";
			for (int i = 0; i < sons.size(); i++) {
				ToIndentLogicNode son = sons.get(i);
				if (i == 0) {
					son.toIndentString00(dent, "", sb);
				} else {
					son.toIndentString00(dent, "", sb);
				}
			}
		} else if (this.op.isEmpty()) {
			sb.append(dent + this.expSt);
		} else if ((this.expSt.length() < 60)) {
			sb.append(dent + this.expSt);
		} else {
			//math expression. eg. a+27
			sb.append(dent + (this.op));
			dent += Dent1;
			dent += " ";
			for (int i = 0; i < sons.size(); i++) {
				ToIndentLogicNode son = sons.get(i);
				if (i == 0) {
					son.toIndentString00(dent, "", sb);
				} else {
					son.toIndentString00(dent, "", sb);
				}
			}
		}
	}

	public String print(int opt) {
		if (opt < 0)
			return "";
		++times;
		Ca.log(1, "\n" + times + "------------" + cont.expSt);
		String ret = toIndentString(1);
		Ca.log(ret);
		return ret;
	}

	void addSon(ToIndentLogicNode son) {
		if (sons == null)
			sons = new ArrayList<>();
		Coms21.asert(!sons.contains(son) && this != son, "");
		son.dad = this;
		sons.add(son);
	}

	void toLineString00(StringBuilder buf) {
		if (this.isExpUpdated) {
			buf.append(this.expSt);
			return;
		} else if (sons == null) {
			buf.append(this.expSt);
			return;
		}
		if (!this.isAnd)
			buf.append("(");
		for (int i = 0; i < sons.size(); i++) {
			if (i > 0)
				buf.append(" " + op + " ");
			ToIndentLogicNode son = sons.get(i);
			son.toLineString00(buf);
		}
		if (!this.isAnd)
			buf.append(")");
	}

	/**
	 * 单行表达。
	 */
	public String toLineString() {
		StringBuilder buf = new StringBuilder();
		this.toLineString00(buf);
		return buf.toString();
	}

	/**
	 * 缩进表达
	 */
	public String toIndentString(int use) {
		if (use < 0)
			return "";
		StringBuilder sb = new StringBuilder();
		this.toIndentString00("\n", "", sb);
		sb.delete(0, 1);
		String st = sb.toString();
		if ("adsf".isEmpty()) {
			st = st.replaceAll("\\|\\s+", "| ");
			st = st.replaceAll("\\+\\s+", "+ ");
		}
		String ret = sb.toString();
		return ret;
	}

	public void setExpSt(String st) {
		this.expSt = st;
	}

	public void setIsAnd(Boolean isAnd) {
		this.isAnd = isAnd;
		if (isAnd == null) {
			this.sons = null;
		}
	}

	public boolean isLeaf() {
		return sons == null;
	}

	public AndOrNode toAndOrNode(AndOrExp root) {
		AndOrNode ret = new AndOrNode(this, root);
		return ret;
	}

	static int times, GenID;

	static final String Dent1 = "    ";
}
